home *** CD-ROM | disk | FTP | other *** search
/ MacWorld 1999 February / Macworld (1999-02).dmg / Cinema 4D GO demo / Gumption Plug-ins / Plug-ins / Freeware / Fillet / FILLET.COF
Text File  |  1998-03-29  |  12KB  |  451 lines

  1. /*
  2.     The fillet plugin generates a set of splines which form a blend between two
  3.     polygon objects. After (manually) setting the direction and starting points
  4.     of each spline, create a Skin object from the group.
  5.     
  6.     Bonus plugins:
  7.         Inflate object - compute average normal at each vertex
  8.         and move outward a specified distance
  9.         
  10.         Derive spline of intersection - draw a spline along the surface of the
  11.         selected object, where it is intersected by the specified cutting object  
  12.         
  13.     Send comments, praise, money :-) etc. to:
  14.     
  15.     Jim Hasselbrook
  16.     hassel@bellatlantic.net
  17.     
  18.     This software is provided free, AS IS. Feel free to modify it to suit your needs.
  19. */
  20.  
  21.  
  22.  
  23. copy(doc, obj, newname)
  24. {
  25.     StatusSetText("Copying object", TRUE);
  26.     
  27.     var i;
  28.     
  29.     var pcount = obj->GetPointCount();
  30.     var ecount = obj->GetEdgeCount();
  31.     var tcount = obj->GetTriangleCount();
  32.     var qcount = obj->GetQuadrangleCount();
  33.  
  34.     var newobj = doc->NewPolygonObject(newname, NULL, NULL, pcount, ecount, tcount, qcount);
  35.  
  36.     var t = new(Triangle);
  37.     var q = new(Quadrangle);
  38.     var e = new(Edge);
  39.     
  40.     var mat = new(Matrix);
  41.     obj->GetMg(mat);            // obj to world coords
  42.     
  43.     for (i=0; i<pcount; i++) {
  44.         StatusSetBar(i*100.0/pcount, TRUE);
  45.         newobj->SetPoint(i, mat->MulP(obj->GetPoint(i)));
  46.     }
  47.     
  48.     for (i=0; i<ecount; i++) {
  49.         StatusSetBar(i*100.0/ecount, TRUE);
  50.         obj->GetEdge(i, e);
  51.         newobj->SetEdge(i, e);
  52.     }
  53.     
  54.     for (i=0; i<tcount; i++) {
  55.         StatusSetBar(i*100.0/tcount, TRUE);
  56.         obj->GetTriangle(i, t);
  57.         newobj->SetTriangle(i, t);
  58.     }
  59.     
  60.     for (i=0; i<qcount; i++) {
  61.         StatusSetBar(i*100.0/qcount, TRUE);
  62.         obj->GetQuadrangle(i, q);
  63.         newobj->SetQuadrangle(i, q);
  64.     }
  65.     
  66.     newobj->UpdateObject();
  67.     return newobj;
  68. }
  69.  
  70. doInflate(doc, obj, amount)
  71. {
  72.     var pcount = obj->GetPointCount();
  73.     var tcount = obj->GetTriangleCount();
  74.     var qcount = obj->GetQuadrangleCount();
  75.     
  76.     StatusSetText("Copying object", TRUE);
  77.     var newobj = copy(doc, obj, stradd(stradd(obj->GetName(), "+"), tostring(amount)));
  78.  
  79.     if (amount != 0.0)
  80.     {
  81.         var i;    
  82.         var norms = new(array, pcount);
  83.         var t = new(Triangle);
  84.         var q = new(Quadrangle);
  85.         var a, b, c, d, norm;
  86.         
  87.         StatusSetText("Calculating normals", TRUE);
  88.         
  89.         for (i=0; i<pcount; i++)
  90.         {
  91.             StatusSetBar(i*100.0/pcount, TRUE);
  92.             norms[i] = vector(0.0, 0.0, 0.0);
  93.         }
  94.         
  95.         for (i=0; i<tcount; i++)
  96.         {
  97.             StatusSetBar(i*100.0/tcount, TRUE);
  98.             newobj->GetTriangle(i, t);
  99.             a = newobj->GetPoint(t->a);
  100.             b = newobj->GetPoint(t->b);
  101.             c = newobj->GetPoint(t->c);
  102.             norm = vnorm(vcross(a-b, a-c));
  103.             norms[t->a] += norm;
  104.             norms[t->b] += norm;
  105.             norms[t->c] += norm;
  106.         }
  107.         
  108.         for (i=0; i<qcount; i++)
  109.         {
  110.             StatusSetBar(i*100.0/qcount, TRUE);
  111.             newobj->GetQuadrangle(i, q);
  112.             a = newobj->GetPoint(q->a);
  113.             b = newobj->GetPoint(q->b);
  114.             c = newobj->GetPoint(q->c);
  115.             d = newobj->GetPoint(q->d);
  116.             norm = vnorm(vcross(a-b, a-c) + vcross(a-c, a-d));
  117.             norms[q->a] += norm;
  118.             norms[q->b] += norm;
  119.             norms[q->c] += norm;
  120.             norms[q->d] += norm;
  121.         }
  122.     
  123.         StatusSetText("Inflating points", TRUE);
  124.         for (i=0; i<pcount; i++)
  125.         {
  126.             StatusSetBar(i*100.0/pcount, TRUE);
  127.             newobj->SetPoint(i, newobj->GetPoint(i) + vnorm(norms[i]) * amount);
  128.         }
  129.     }
  130.     newobj->UpdateObject();
  131.     
  132.     return newobj;
  133. }
  134.  
  135. isInside(p,a,b,c)
  136. {
  137.     var vab = vcross(a-p, b-p);    
  138.     var vbc = vcross(b-p, c-p);    
  139.     var vca = vcross(c-p, a-p);
  140.     
  141.     // for accuracy, choose component with greatest magnitude
  142.     
  143.     if (abs(vab.x) > abs(vab.y) && (abs(vab.x) > abs(vab.z)))    // x
  144.     {
  145.         return (vab.x >= 0.0 && vbc.x >= 0.0 && vca.x >= 0.0) || (vab.x <= 0.0 && vbc.x <= 0.0 && vca.x <= 0.0);
  146.     }
  147.     else if (abs(vab.y) > abs(vab.z)) // y
  148.     {
  149.         return (vab.y >= 0.0 && vbc.y >= 0.0 && vca.y >= 0.0) || (vab.y <= 0.0 && vbc.y <= 0.0 && vca.y <= 0.0);
  150.     }
  151.     else // z
  152.     {
  153.         return (vab.z >= 0.0 && vbc.z >= 0.0 && vca.z >= 0.0) || (vab.z <= 0.0 && vbc.z <= 0.0 && vca.z <= 0.0);
  154.     }
  155. }
  156.  
  157. edgeIntersectsTriangle(ea, eb, a, b, c)
  158. {
  159.     var v = vnorm(vcross(a-b, a-c));    
  160.     var w = eb - ea;
  161.     var wv = w * v;    // dot product
  162.     
  163.     if (wv)    // if zero, edge is || to plane of triangle
  164.     {
  165.         var x = ((a - ea) * v) / wv;
  166.         
  167.         if (0.0 <= x && x <= 1.0)    // intersection with plane lies between e->a and e->b
  168.         {
  169.             var p = ea + x * w;
  170.             
  171.             if (isInside(p, a, b, c))    // point lies inside triangle
  172.             {
  173.                 return p;
  174.             }
  175.         }
  176.     }
  177.     return NULL;
  178. }
  179.  
  180. /*
  181.     Spline point sorting, courtesy of John Detch, 3D Gear
  182. */
  183.  
  184. ClosePointSort(obj)
  185. {
  186.         var sp1=new(SplinePoint),sp2=new(SplinePoint);
  187.         var num=obj->GetPointCount();
  188.         var i,j,last=new(SplinePoint);
  189.         var len,nlen,v=vector(0,0,0);
  190.  
  191.         StatusSetText("Sorting spline points", TRUE);
  192.         for(i=0;i<num;i++){
  193.                 obj->GetPoint(i,sp1);
  194.                 len=100000000.0;
  195.                 for(j=i+1;j<num;j++){
  196.                         obj->GetPoint(j,sp2);
  197.                         v=sp1->p-sp2->p;
  198.                         nlen=vlen(v);
  199.                         if(nlen<len){
  200.                                 last=j;
  201.                                 len=nlen;
  202.                         }
  203.                 }
  204.                 if(j!=i+1){
  205.                         obj->GetPoint(i+1,sp1);
  206.                         obj->GetPoint(last,sp2);
  207.                         obj->SetPoint(i+1,sp2);
  208.                         obj->SetPoint(last,sp1);
  209.                 }
  210.         }
  211. }
  212.  
  213. /*
  214.     for each edge in objA
  215.     {
  216.         find edges of objA which intersect some polyB of objB, get point of intersection
  217.     }
  218. */
  219. cut_A_with_B(doc, group, objA, objB)
  220. {
  221.     var i, j;
  222.     var splineobj = doc->NewSplineObject("Spline",group,NULL,0);
  223.     var newPoint = new(SplinePoint);
  224.     var info = new(SplineInfo);
  225.     splineobj->GetSplineInfo(info);
  226.     info->closed = TRUE;
  227.     splineobj->SetSplineInfo(info);
  228.     
  229.     var lowerA = vector(0.0, 0.0, 0.0);
  230.     var upperA = vector(0.0, 0.0, 0.0);
  231.     var lowerB = objB->GetBox1();
  232.     var upperB = objB->GetBox2();
  233.     
  234.     var ecountA = objA->GetEdgeCount();
  235.     var tcountB = objB->GetTriangleCount();
  236.     var qcountB = objB->GetQuadrangleCount();
  237.     var e = new(Edge);
  238.     var t = new(Triangle);
  239.     var q = new(Quadrangle);
  240.     var a, b, c, d, ea, eb, p, lastPoint;
  241.     
  242.     var ta = new(array, tcountB);
  243.     var tb = new(array, tcountB);
  244.     var tc = new(array, tcountB);
  245.     var qa = new(array, qcountB);
  246.     var qb = new(array, qcountB);
  247.     var qc = new(array, qcountB);
  248.     var qd = new(array, qcountB);
  249.     
  250.     for (i=0; i<tcountB; i++)
  251.     {
  252.         objB->GetTriangle(i, t);
  253.         ta[i] = objB->GetPoint(t->a);
  254.         tb[i] = objB->GetPoint(t->b);
  255.         tc[i] = objB->GetPoint(t->c);
  256.     }
  257.     
  258.     for (i=0; i<qcountB; i++)
  259.     {
  260.         objB->GetQuadrangle(i, q);
  261.         qa[i] = objB->GetPoint(q->a);
  262.         qb[i] = objB->GetPoint(q->b);
  263.         qc[i] = objB->GetPoint(q->c);
  264.         qd[i] = objB->GetPoint(q->d);
  265.     }
  266.     
  267.     StatusSetText("Finding intersection", TRUE);
  268.     for (i=0; i<ecountA; i++)
  269.     {
  270.         StatusSetBar(i*100.0/ecountA, TRUE);
  271.         objA->GetEdge(i, e);
  272.         ea = objA->GetPoint(e->a);
  273.         eb = objA->GetPoint(e->b);
  274.         
  275.         if ((ea.x < lowerB.x && eb.x < lowerB.x) || (ea.x > upperB.x && eb.x > upperB.x)) continue;
  276.         if ((ea.y < lowerB.y && eb.y < lowerB.y) || (ea.y > upperB.y && eb.y > upperB.y)) continue;
  277.         if ((ea.z < lowerB.z && eb.z < lowerB.z) || (ea.z > upperB.z && eb.z > upperB.z)) continue;
  278.         
  279.         if (ea.x < eb.x) { lowerA.x = ea.x; upperA.x = eb.x; } else { lowerA.x = eb.x; upperA.x = ea.x; }
  280.         if (ea.y < eb.y) { lowerA.y = ea.y; upperA.y = eb.y; } else { lowerA.y = eb.y; upperA.y = ea.y; }
  281.         if (ea.z < eb.z) { lowerA.z = ea.z; upperA.z = eb.z; } else { lowerA.z = eb.z; upperA.z = ea.z; }
  282.         
  283.         for (j=0; j<tcountB; j++)
  284.         {
  285.             a = ta[j]; b = tb[j]; c = tc[j];
  286.             
  287.             if ((a.x < lowerA.x && b.x < lowerA.x && c.x < lowerA.x) ||
  288.                 (a.x > upperA.x && b.x > upperA.x && c.x > upperA.x)) continue;
  289.             if ((a.y < lowerA.y && b.y < lowerA.y && c.y < lowerA.y) ||
  290.                 (a.y > upperA.y && b.y > upperA.y && c.y > upperA.y)) continue;
  291.             if ((a.z < lowerA.z && b.z < lowerA.z && c.z < lowerA.z) ||
  292.                 (a.z > upperA.z && b.z > upperA.z && c.z > upperA.z)) continue;
  293.                 
  294.             p = edgeIntersectsTriangle(ea, eb, a, b, c);
  295.             if (p && (!lastPoint || p != lastPoint))
  296.             {
  297.                 newPoint->p = p;
  298.                 splineobj->AddPoint(newPoint);
  299.                 lastPoint = p;
  300.             }
  301.         }
  302.         
  303.         for (j=0; j<qcountB; j++)
  304.         {
  305.             a = qa[j]; b = qb[j]; c = qc[j]; d = qd[j];
  306.             
  307.             if ((a.x < lowerA.x && b.x < lowerA.x && c.x < lowerA.x && d.x < lowerA.x) ||
  308.                 (a.x > upperA.x && b.x > upperA.x && c.x > upperA.x && d.x > upperA.x)) continue;
  309.             if ((a.y < lowerA.y && b.y < lowerA.y && c.y < lowerA.y && d.y < lowerA.y) ||
  310.                 (a.y > upperA.y && b.y > upperA.y && c.y > upperA.y && d.y > upperA.y)) continue;
  311.             if ((a.z < lowerA.z && b.z < lowerA.z && c.z < lowerA.z && d.z < lowerA.z) ||
  312.                 (a.z > upperA.z && b.z > upperA.z && c.z > upperA.z && d.z > upperA.z)) continue;
  313.                 
  314.             p = edgeIntersectsTriangle(ea, eb, a, b, c);
  315.             if (p && (!lastPoint || p != lastPoint))
  316.             {
  317.                 newPoint->p = p;
  318.                 splineobj->AddPoint(newPoint);
  319.                 lastPoint = p;
  320.             }
  321.             p = edgeIntersectsTriangle(ea, eb, c, d, a);
  322.             if (p && (!lastPoint || p != lastPoint))
  323.             {
  324.                 newPoint->p = p;
  325.                 splineobj->AddPoint(newPoint);
  326.                 lastPoint = p;
  327.             }
  328.         }
  329.     }
  330.     ClosePointSort(splineobj);
  331.     splineobj->UpdateObject();
  332. }
  333.  
  334. inflate(doc)
  335. {
  336.     var obj = doc->FindFirstActiveObject();
  337.     
  338.     if(!obj || !instanceof(obj,PolygonObject)) 
  339.     {
  340.         TextDialog("Must select a polygon object!", DLG_OK);
  341.         return;
  342.     }
  343.     
  344.     var d = new (SimpleDialog);
  345.  
  346.     d->SetData(0,"Amount",FIELD_FLOAT,-1000000.0,1000000.0,10.0);
  347.     d->SetTitle("Inflate by Amount");
  348.  
  349.     if (!d->DoDialog()) return FALSE;
  350.  
  351.     var amount = d->GetData(0);
  352.     
  353.     doInflate(doc, obj, amount);
  354.     
  355.     doc->SendMessage(DOCUMENT_CHANGED);
  356.     StatusClear();
  357. }
  358.  
  359. intersect(doc)
  360. {
  361.     var objA = doc->FindFirstActiveObject();
  362.     
  363.     if(!objA || !instanceof(objA, PolygonObject)) 
  364.     {
  365.         TextDialog("Must select a polygon object!", DLG_OK);
  366.         return;
  367.     }
  368.     
  369.     var d = new (SimpleDialog);
  370.  
  371.     d->SetData(0,"Cutting Object",FIELD_STRING,0.0,0.0,"");
  372.     d->SetTitle("Derive Spline of Intersection");
  373.  
  374.     if (!d->DoDialog()) return FALSE;
  375.  
  376.     var objB = doc->FindObject(d->GetData(0));
  377.     
  378.     if(!objB || !instanceof(objB, PolygonObject)) 
  379.     {
  380.         TextDialog("Must select a polygon object to cut with!", DLG_OK);
  381.         return;
  382.     }
  383.     
  384.     var copyA = copy(doc, objA, "tempA");
  385.     var copyB = copy(doc, objB, "tempB");
  386.  
  387.     cut_A_with_B(doc, NULL, copyA, copyB);
  388.  
  389.     doc->KillObject(copyA);
  390.     doc->KillObject(copyB);
  391.     doc->SendMessage(DOCUMENT_CHANGED);
  392.     StatusClear();
  393. }
  394.  
  395. fillet(doc)
  396. {
  397.     var d = new (SimpleDialog);
  398.  
  399.     d->SetData(0,"Object A",FIELD_STRING,0.0,0.0,"");
  400.     d->SetData(1,"Object B",FIELD_STRING,0.0,0.0,"");
  401.     d->SetData(2,"Fillet Radius",FIELD_FLOAT,-1000000.0,1000000.0,20.0);
  402.     d->SetData(3,"Number of Steps",FIELD_INTEGER,1,20,1);
  403.     d->SetTitle("Derive Fillet Splines");
  404.  
  405.     if (!d->DoDialog()) return FALSE;
  406.  
  407.     var objA = doc->FindObject(d->GetData(0));
  408.     var objB = doc->FindObject(d->GetData(1));
  409.     var radius = d->GetData(2);
  410.     var steps = d->GetData(3);
  411.     
  412.     if(!objA || !objB || !instanceof(objA, PolygonObject) || !instanceof(objB, PolygonObject)) 
  413.     {
  414.         TextDialog("Must select two polygon objects!", DLG_OK);
  415.         return;
  416.     }
  417.     
  418.     var group = doc->NewPolygonObject("Fillet Group",NULL,NULL,0,0,0,0);
  419.     
  420.     var i;
  421.     
  422.     for (i=0; i<=steps; i++)
  423.     {
  424.         var angle = (PI / 2.0) * float(i) / float(steps);
  425.         var copyA = doInflate(doc, objA, (1 - cos(angle)) * radius);
  426.         var copyB = doInflate(doc, objB, (1 - sin(angle)) * radius);
  427.  
  428.         if (i < (steps+1)/2)
  429.         {
  430.             cut_A_with_B(doc, group, copyA, copyB);
  431.         }
  432.         else
  433.         {
  434.             cut_A_with_B(doc, group, copyB, copyA);
  435.         }
  436.         
  437.         doc->KillObject(copyA);
  438.         doc->KillObject(copyB);
  439.     }
  440.     group->UpdateObject();
  441.     doc->SendMessage(DOCUMENT_CHANGED);
  442.     StatusClear();
  443. }
  444.  
  445. main()
  446. {
  447.     RegisterMenuHook("Spline of Intersection","intersect");
  448.     RegisterMenuHook("Inflate","inflate");
  449.     RegisterMenuHook("Fillet","fillet");
  450. }
  451.